
import 'package:flutter/widgets.dart';

import 'dva-hooks.dart';
import 'dva-model.dart';
import 'redux.dart';

class Props {

  Props({String? namespace}) {
    this.namespace = namespace;
  }

  String? namespace;

  Future<dynamic> dispatch(String key, {
    dynamic payload,
    String? namespace
  }) async {
    String? _key = key;
    if(namespace != null) _key = '$namespace/$key';
    if(this.namespace != null) _key = '${this.namespace}/$key';
    return await store.dispatch(DvaAction(_key, payload));    
  }

  Store get store {
    return Provider().store!;
  }
}

abstract class Connect<T extends StatefulWidget> extends State<T> {
  List<String>? listenKeys;
  // 继承后用于过滤变化监听
  Map<String, StoreOfState> mapState(Map<String, StoreOfState> rootState) {
    return rootState;
  }

  Connect() {
    store = Provider().store;
    state = mapState(store!.rootState);
    props = new Props();
  }

  Props? props;
  Map<String, StoreOfState>? state;
  Store? store;
  StateChangeFunction? storeSubCallback;
  bool _mounted = false;

  @mustCallSuper
  @override
  void initState() { 
    super.initState();
    storeSubCallback = (StoreOfState lState, StoreOfState nState) {
      if(listenKeys != null && listenKeys?.contains(lState.namespace) != true) return;
      if(state?.containsKey(nState.namespace) != true) return;
      if(!shouldUpdate(lState, nState)) return;
      setState(()=><String, Function>{});
    };
    store!.subscribe(storeSubCallback!);
    if(_mounted == false) {
      Future<dynamic>.delayed(Duration(milliseconds: 10))..whenComplete((){
        didMounted();
      });
    }
  }
  /// 建议加载数据
  @mustCallSuper
  void didMounted() {
    _mounted = true;
  }

  @mustCallSuper
  @override
  void dispose() {
    store!.remove(storeSubCallback!);
    _mounted = false;
    super.dispose();
  }

  bool shouldUpdate(StoreOfState lState, StoreOfState nState) => lState.updateAt?.millisecondsSinceEpoch != nState.updateAt?.millisecondsSinceEpoch;
}

class Provider {
   /// 内部构造方法，可避免外部暴露构造函数，进行实例化
  Provider._internal();

  /// 工厂构造方法，这里使用命名构造函数方式进行声明
  factory Provider() => _getInstance();

    /// 单例对象
  static Provider? _instance;

  Store? store;

  /// 获取单例内部方法
  static Provider _getInstance() {
    // 只能有一个实例
    _instance ??= Provider._internal();
    return _instance!;
  }
}

class DvaReducer<T> extends Reducer<T> {
  DvaReducer({ 
    this.model, 
    Store? store, 
    this.useEffect, 
    StateChangeEventFunction? onStateChange
  }) : 
    super(store: store, onStateChange: onStateChange) {
    if(model?.state!= null) {
      initState = model!.state;
    }
  }
  Model<T>? model;
  UseEffectFunction? useEffect;

  @override
  StateChangeEventFunction? get onStateChange => super.onStateChange;

  @override
  set onStateChange(StateChangeEventFunction? value) {
    super.onStateChange = value;
  }
 
  @override
  bool broadcast() {
    final Map<String, StoreOfState<dynamic>> nextStateMap = store!.getState();
    final StoreOfState<T> nextState = nextStateMap[model!.namespace] as StoreOfState<T>;
    if(canUpdate(lastState, nextState)) {
      store!.broadcast(lastState, nextState);
      lastState = nextState.clone();
      return true;
    }
    return false;
  }
  
  @override
  Future<StoreOfState<T>> runReducer(StoreOfState<T> state, DvaAction action) async {
    final String type = action.type;
    if(type.contains('/') && type.split('/').first == model!.namespace) {
      final String key = type.split('/').last;
      if(model!.reducers.containsKey(key)) {
        final StoreOfState newState = model!.reducers[key]!(state as StoreOfState<DvaState>, action);
        if(broadcast() && onStateChange != null) {
          onStateChange!(nextState, action);
        }
        return newState as StoreOfState<T>;
      }
      if(model!.effects.containsKey(key)) {
        final DispatchFunction dispatch = store!.dispatch;
        final StoreGetStateFunction getState = store!.getState;
        final EffectFunction effect = model!.effects[key]!;
        if(useEffect != null) {
          await useEffect!(effect)(action, ModelEffects(dispatch, getState, model!.namespace));
        } else {
          await effect(action, ModelEffects(dispatch, getState, model!.namespace));
        }     
      }
    }
    return state;
  }
}

Future<dynamic> dvaDispatch(String type, dynamic payload) async {
  return await Provider().store!.dispatch(DvaAction(type, payload));
}

T dvaGetStateByKey<T>(String key) {
  return Provider().store!.getStateByKey<T>(key);
}

